#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include "log.hpp"
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <cstring>
#include <functional>

// 相当于函数指针。参数是string，返回值也是string
using func_t = std::function<std::string(const std::string &)>;

log lg;

#define DEFAULT_IP "127.0.0.1" // 默认IP地址
#define DEFAULT_PORT 8080      // 默认端口号

enum
{
    SOCKET_ERR = 1,
    BIND_ERR
};

class UdpServer
{
public:
    UdpServer(const uint16_t &port = DEFAULT_PORT, const std::string &ip = DEFAULT_IP)
        : _socketfd(-1), _ip(ip), _port(port), _isRun(false)
    {
    }

    ~UdpServer()
    {
        if (_socketfd >= 0)
        {
            close(_socketfd);
        }
        // 其实关不关都无所谓，因为只要调用析构函数，说明对象销毁了，也就代表UDP服务器挂掉了
        // 而文件生命周期是随进程的
    }

    // 初始化UDP服务器
    void Init()
    {
        /*
            1. 创建套接字
            AF_INET - 用于IPv4 或 AF_INET6 - 用于IPv6。都表示跨网络通信
            SOCK_DGRAM - 用于UDP协议
            第三个默认填0即可
        */
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0) // 套接字创建失败
        {
            lg.logmessage(Fatal, "socket create error, socketfd: %d", _socketfd);
            exit(SOCKET_ERR);
        }
        lg.logmessage(Info, "socket create success, socketfd: %d", _socketfd);

        /*
            2. 绑定套接字
        */
        struct sockaddr_in local;
        // 初始化struct sockaddr_in对象（清空）
        bzero(&local, sizeof(local));
        // 填充struct sockaddr_in对象字段
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);                  // 细节
        local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 细节
        // local.sin_addr.s_addr = INADDR_ANY;
        //  绑定
        int n = bind(_socketfd, (const struct sockaddr *)&local, sizeof(local));
        if (n == -1) // 绑定失败
        {
            lg.logmessage(Fatal, "bind error, errno: %d, describe: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg.logmessage(Info, "bind success, errno: %d, describe: %s", errno, strerror(errno));
    }

    // 运行服务端服务器
    void Run(func_t func)
    {
        _isRun = true;
        char buffer[1024];
        while (_isRun)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            ssize_t n = recvfrom(_socketfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);

            if (n < 0) // 发送失败
            {
                // UDP是不可靠的，发送失败不是什么很大的问题，日志级别给个警告就好
                lg.logmessage(Warning, "recvfrom error, errno: %d, describe: %s", errno, strerror(errno));
                continue;
            }

            // 来到这，发送方的数据都在buffer里了
            buffer[n] = 0;

            // 加工：服务端个性化回显
            std::string message = buffer;

            // std::string echo_string = "server echo# " + message;
            // std::cout << echo_string << std::endl;

            // 意思就是我接收到了数据，但我不在该函数内部处理数据，而是在外部
            std::string echo_string = func(message);
            std::cout << echo_string << std::endl;

            // 发送回给对方
            sendto(_socketfd, echo_string.c_str(), echo_string.size(), 0, (const sockaddr *)&client, len);
        }
    }

private:
    int _socketfd;   // 网络文件描述符
    uint16_t _port;  // UDP服务器的端口号
    std::string _ip; // UDP服务器的IP地址
    bool _isRun;     // 服务器是否在运行
};